package com.minelx.sequence.core;

import java.util.Arrays;
import java.util.Iterator;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

public interface ReturnMatcher<R, T> {

	boolean isMatch(T clue);

	R onMatched(T clue);

	default boolean match(T clue) {
		if (!isMatch(clue)) {
			return false;
		}
		onMatched(clue);
		return true;
	}

	default ReturnMatcher<R, T> or(ReturnMatcher<R, T> target) {
		return OrMatcher.create(Arrays.asList(this, target));
	}

	default Optional<R> get(T clue) {
		if (isMatch(clue)) {
			return Optional.of(onMatched(clue));
		}
		return Optional.empty();
	}

	default Iterator<R> iterator(T from) {
		return new Iterator<R>() {
			@Override
			public boolean hasNext() {
				return isMatch(from);
			}

			@Override
			public R next() {
				return onMatched(from);
			}
		};
	}

	static <R, T> ReturnMatcher<R, T> create(Predicate<T> isMatch,
	                                         Function<T, R> onMatched) {
		return new ReturnMatcher<R, T>() {
			@Override
			public boolean isMatch(T clue) {
				return isMatch.test(clue);
			}

			@Override
			public R onMatched(T clue) {
				return onMatched.apply(clue);
			}
		};
	}

	static <R, T> ReturnMatcher<R, T> always(R returns) {
		return create(clue -> true, clue -> returns);
	}

	static <R, T> ReturnMatcher<R, T> always(Function<T, R> mapper) {
		return create(clue -> true, mapper);
	}

	static <R, T> ReturnMatcher<R, T> never() {
		return create(clue -> false, clue -> {
			throw new RuntimeException("unsupported operation.");
		});
	}

	default ReturnMatcher<?, T> map(Function<R, ?> mapper) {
		return ReturnMatcher.create(
				this::isMatch,
				scanner -> mapper.apply(onMatched(scanner))
		);
	}
}
